#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "xlibrary.h"

#include "analyse.h"

void mexit(int ret, char *format, ...) {	// common exit handler
	if(format) {
		va_list ap;
		va_start(ap, format);
		vprintf(format, ap);
		va_end(ap);
	}

	exit(ret);
}

int *count_reloc(void *b, unsigned long bs, void *dS, void *dE, void *iS, void *iE, void *gS, void *gE, void *bS, void *bE) {
	static int n[4*5];
	bzero(n, sizeof(int)*4*5);

	// from local address to Oxbf80####
	#define fixoffset(l) ((void *)(l)-b+dS)

	void **addr;
	int i=0;
	for(addr = (void**)(b-dS+iS); addr < (void**)(b+bs); addr++) {
		if(fixoffset(addr)==iE) i++;
		if(fixoffset(addr)==gS) i++;
		if(fixoffset(addr)==gE) i++;
		if(fixoffset(addr)==bS) i++;

		if(dS <= *addr && *addr < bE) {
			     if(*addr < iE) n[i*5+0]++;
			else if(*addr < gS) n[i*5+1]++;
			else if(*addr < gE) n[i*5+2]++;
			else if(*addr < bS) n[i*5+3]++;
			else                n[i*5+4]++;
		}
	}

	#undef fixoffset

	return n;
}

int process(char *fn) {	// process a file
	printf("--- %s ---", basename(fn));

	FILE *fd=fopen(fn, "rb");
	if(!fd) return 1;

	fseek(fd, 0, SEEK_END);
	unsigned long fs=ftell(fd);
	rewind(fd);

	printf("\nsize=%d", (int)fs);

	if(fs<MAX(sizeof(sLHeader), sizeof(sPHeader))) return 1;

	void *fbuf=xmalloc(fs);
	if(fread(fbuf, 1, fs, fd)!=fs) {
		xfree(fbuf);
		fclose(fd);
		return 1;
	}

	fclose(fd);

	void *dS=NULL, *dE=NULL, *iS=NULL, *iE=NULL, *gS=NULL, *gE=NULL, *bS=NULL, *bE=NULL;
	int *n=NULL, type;

	if(!strcmp(((sLHeader *)fbuf)->magic, "SunOS_library")) type=0;
	else if(!strcmp(((sPHeader *)fbuf)->magic, "SunOS_plugin")) type=1;
	else {
		xfree(fbuf);
		return 1;
	}

	printf("\ntype=");
	switch(type) {
	case 0:
		printf("library");
		sLHeader *lh=(sLHeader *)fbuf;

		dS=lh->dataStart;
		dE=lh->dataEnd;
		iS=lh->interworkStart;
		iE=lh->interworkEnd;
		gS=lh->gotStart;
		gE=lh->gotEnd;
		bS=lh->bssStart;
		bE=lh->bssEnd;
		break;
	case 1:
		printf("plugin");
		sPHeader *ph=(sPHeader *)fbuf;

		dS=ph->dataStart;
		dE=ph->dataEnd;
		iS=ph->interworkStart;
		iE=ph->interworkEnd;
		gS=ph->gotStart;
		gE=ph->gotEnd;
		bS=ph->bssStart;
		bE=ph->bssEnd;
		break;
	}

	printf("\nsections={");
	switch(type) {
	case 0:
	case 1:
		n=count_reloc(fbuf, fs, dS, dE, iS, iE, gS, gE, bS, bE);

		#define TO(i) do { \
			if(!(n[i*5+0]+n[i*5+1]+n[i*5+2]+n[i*5+3]+n[i*5+4])) break; \
			printf(", relocatable address in this section that points to :"); \
			if(n[i*5+0]) printf("\n    interwork : %d", n[i*5+0]); \
			if(n[i*5+1]) printf("\n    code      : %d", n[i*5+1]); \
			if(n[i*5+2]) printf("\n    got       : %d", n[i*5+2]); \
			if(n[i*5+3]) printf("\n    data      : %d", n[i*5+3]); \
			if(n[i*5+4]) printf("\n    bss       : %d", n[i*5+4]); \
		} while(0)

		printf("\n  header    : 0x%08x ~ 0x%08x (%d)", (unsigned int)dS, (unsigned int)iS, iS-dS);
		printf("\n  interwork : 0x%08x ~ 0x%08x (%d)", (unsigned int)iS, (unsigned int)iE, iE-iS);
		if(iE-iS) TO(0);
		printf("\n  code      : 0x%08x ~ 0x%08x (%d)", (unsigned int)iE, (unsigned int)gS, gS-iE);
		if(gS-iE) TO(1);
		printf("\n  got       : 0x%08x ~ 0x%08x (%d)", (unsigned int)gS, (unsigned int)gE, gE-gS);
		if(gE-gS) TO(2);
		printf("\n  data      : 0x%08x ~ 0x%08x (%d)", (unsigned int)gE, (unsigned int)dE, dE-gE);
		if(dE-gE) TO(3);
		printf("\n  bss       : 0x%08x ~ 0x%08x (%d)", (unsigned int)bS, (unsigned int)bE, bE-bS);

		#undef TO

		break;
	}
	printf("\n}");

	printf("\nfunctions={");
	switch(type) {
	case 0: {
			sLHeader *lh=(sLHeader *)fbuf;
			printf("\n  init   : 0x%08x", (unsigned int)lh->init);
			printf("\n  query  : 0x%08x", (unsigned int)lh->query);
			printf("\n  deinit : 0x%08x", (unsigned int)lh->deinit);
			break;
		}
	case 1: {
			sPHeader *ph=(sPHeader *)fbuf;
			printf("\n  init   : 0x%08x", (unsigned int)ph->init);
			printf("\n  query  : 0x%08x", (unsigned int)ph->query);
			printf("\n  main   : 0x%08x", (unsigned int)ph->main);
			printf("\n  deinit : 0x%08x", (unsigned int)ph->deinit);
			break;
		}
	}
	printf("\n}");

	printf("\n");

	return 0;
}

int main(int argc, char *argv[]) {
	if(argc<2) mexit(1, "Usage :\n\t%s <SunOS binary files>\n", argv[0]);

	fprintf(stderr, "Welcome to the SunOS binary analyser by Ludo6431 v1.0\n");

	while(argc-->1)
		if(process(*(++argv))) {	// process each file
			printf("error\n");
		}

	mexit(0, NULL);	// exit 0 on success
}

